// main.cc 
//	Driver code to initialize, selftest, and run the 
//	operating system kernel.  
//
// Usage: nachos -d <debugflags> -rs <random seed #>
//              -s -x <nachos file> -ci <consoleIn> -co <consoleOut>
//              -f -cp <unix file> <nachos file>
//              -p <nachos file> -r <nachos file> -l -D
//              -ld <nachos directory> -mkdir <nachos directory>
//              -rmdir <nachos directory>
//              -n <network reliability> -m <machine id>
//              -z -K -C -N
//
//    -d causes certain debugging messages to be printed (see debug.h)
//    -rs causes Yield to occur at random (but repeatable) spots
//    -z prints the copyright message
//    -s causes user programs to be executed in single-step mode
//    -x runs a user program
//    -ci specify file for console input (stdin is the default)
//    -co specify file for console output (stdout is the default)
//    -n sets the network reliability
//    -m sets this machine's host id (needed for the network)
//    -K run a simple self test of kernel threads and synchronization
//    -C run an interactive console test
//    -N run a two-machine network test (see Kernel::NetworkTest)
//
//    Filesystem-related flags:
//    -f forces the Nachos disk to be formatted
//    -cp copies a file from UNIX to Nachos
//    -p prints a Nachos file to stdout
//    -r removes a Nachos file from the file system
//    -l lists the contents of the Nachos directory
//    -ld lists the contents of specific directory
//    -D prints the contents of the entire file system 
//    ****I have add some order follow -- bl
//    -touch create a new file    
//    -mv change the file name
//    -cph copies a file in the Nachos filesystem
//    -mkdir make directory.
//    -rmdir remove directory.
//
//  Note: the file system flags are not used if the stub filesystem
//        is being used
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#define MAIN
#include "copyright.h"
#undef MAIN

#undef FILESYS_STUB

#include "main.h"
#include "filesys.h"
#include "openfile.h"
#include "sysdep.h"

// global variables
Kernel *kernel;
Debug *debug;


//----------------------------------------------------------------------
// Cleanup
//	Delete kernel data structures; called when user hits "ctl-C".
//----------------------------------------------------------------------

static void
Cleanup(int x)
{
    cerr << "\nCleaning up after signal " << x << "\n";
    delete kernel;
}



//----------------------------------------------------------------------
// main
// 	Bootstrap the operating system kernel.  
//	
//	Initialize kernel data structures
//	Call some test routines
//	Call "Run" to start an initial user program running
//
//	"argc" is the number of command line arguments (including the name
//		of the command) -- ex: "nachos -d +" -> argc = 3 
//	"argv" is an array of strings, one for each command line argument
//		ex: "nachos -d +" -> argv = {"nachos", "-d", "+"}
//----------------------------------------------------------------------

int
main(int argc, char **argv)
{
    int i;
    char *debugArg = "";
    char *userProgName = NULL;        // default is not to execute a user prog
    bool threadTestFlag = false;
    bool schedulerTestFlag = false;
    bool synchTestFlag = false;
    bool consoleTestFlag = false;
    bool networkTestFlag = false;
    bool shellFlag = false;
#ifndef FILESYS_STUB
    int initialSize = 0;
    char *createFileName = NULL;      // Nachos create a new file
    char *copyUnixFileName = NULL;    // UNIX file to be copied into Nachos
    char *copyNachosFileName = NULL;  // name of copied file in Nachos
    char *copyNachosFile1 = NULL;     // Nachos file to be copied
    char *copyNachosFile2 = NULL;     // name of copied file in Nachos
    char *nachosfileName1 = NULL;     // name of the old file
    char *nachosfileName2 = NULL;     // name of the new file
    char *appendUnixFileName = NULL;  // UNIX file to be appended into Nachos
    char *appendNachosFileName = NULL;// name of appended file in nachos
    char *printFileName = NULL;
    char *removeFileName = NULL;
    bool dirListFlag = false;
    bool dumpFlag = false;
    char *lsDirName = NULL;
    char *mkDirName = NULL;
    char *rmDirName = NULL;
#endif //FILESYS_STUB

    // some command line arguments are handled here.
    // those that set kernel parameters are handled in
    // the Kernel constructor
    for (i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-d") == 0) {
	    ASSERT(i + 1 < argc);   // next argument is debug string
            debugArg = argv[i + 1];
	    i++;
	}
	else if (strcmp(argv[i], "-z") == 0) {
            cout << copyright << "\n";
	}
	else if (strcmp(argv[i],"-shell") == 0)
	{
		shellFlag = TRUE;
	}
	else if (strcmp(argv[i], "-x") == 0) {
	    ASSERT(i + 1 < argc);
	    userProgName = argv[i + 1];
	    i++;
	}
	else if (strcmp(argv[i], "-K") == 0) {
	    threadTestFlag = TRUE;
	}

	else if (strcmp(argv[i], "-S") == 0) {
		    schedulerTestFlag = TRUE;
	}
	else if (strcmp(argv[i], "-PCS") == 0) {
		synchTestFlag = TRUE;
	}
	else if (strcmp(argv[i], "-C") == 0) {
	    consoleTestFlag = TRUE;
	}
	else if (strcmp(argv[i], "-N") == 0) {
	    networkTestFlag = TRUE;
	}
#ifndef FILESYS_STUB
	else if (strcmp(argv[i], "-touch") == 0) {
	    ASSERT(i + 1 < argc);
	    createFileName = argv[i + 1];
	    i++;
	}
	else if (strcmp(argv[i], "-cp") == 0) {
	    ASSERT(i + 2 < argc);
	    copyUnixFileName = argv[i + 1];
	    copyNachosFileName = argv[i + 2];
	    i += 2;
	}
    else if (strcmp(argv[i], "-cph") == 0) {
        ASSERT(i + 2 < argc);
        copyNachosFile1 = argv[i + 1];
        copyNachosFile2 = argv[i + 2];
        i += 2;
    }
    else if (strcmp(argv[i], "-mv") == 0) {
        ASSERT(i + 2 < argc);
        nachosfileName1 = argv[i + 1];
        nachosfileName2 = argv[i + 2];
        i += 2;
    }
	else if (strcmp(argv[i], "-ap") == 0) {
		ASSERT(i + 2 < argc);
		appendUnixFileName = argv[i + 1];
		appendNachosFileName = argv[i + 2];
		i += 2;
	}
	else if (strcmp(argv[i], "-p") == 0) {
	    ASSERT(i + 1 < argc);
	    printFileName = argv[i + 1];
	    i++;
	}
	else if (strcmp(argv[i], "-r") == 0) {
	    ASSERT(i + 1 < argc);
	    removeFileName = argv[i + 1];
	    i++;
	}
	else if (strcmp(argv[i], "-l") == 0) {
	    dirListFlag = true;
	}
	else if (strcmp(argv[i], "-ld") == 0) {
	    ASSERT(i + 1 < argc);
	    lsDirName = argv[i + 1];
	    i++;
	}
	else if (strcmp(argv[i], "-D") == 0) {
	    dumpFlag = true;
	}

	else if (strcmp(argv[i], "-mkdir") == 0) {
	    ASSERT(i + 1 < argc);
	    mkDirName = argv[i + 1];
	    i++;
	}
	else if (strcmp(argv[i], "-rmdir") == 0) {
	    ASSERT(i + 1 < argc);
	    rmDirName = argv[i + 1];
	    i++;
	}
#endif //FILESYS_STUB
	else if (strcmp(argv[i], "-u") == 0) {
            cout << "Partial usage: nachos [-z -d debugFlags]\n";
            cout << "Partial usage: nachos [-x programName]\n";
	    cout << "Partial usage: nachos [-K] [-C] [-N]\n";
#ifndef FILESYS_STUB
            cout << "Partial usage: nachos [-touch NachosFile]\n";
            cout << "Partial usage: nachos [-cp UnixFile NachosFile]\n";
            cout << "Partial usage: nachos [-cph NachosFile NachosFile]\n";
            cout << "Partial usage: nachos [-mv NachosFile NachosFile]\n";
            cout << "Partial usage: nachos [-p fileName] [-r fileName]\n";
            cout << "Partial usage: nachos [-l] [-D]\n";
#endif //FILESYS_STUB
	}

    }
    debug = new Debug(debugArg);

    DEBUG(dbgThread, "Entering main");

    kernel = new Kernel(argc, argv);

    kernel->Initialize();

    CallOnUserAbort(Cleanup);		// if user hits ctl-C

    // at this point, the kernel is ready to do something
    // run some tests, if requested
    if (threadTestFlag) {
      kernel->ThreadSelfTest();  // test threads and synchronization
    }

    if (schedulerTestFlag) {
             kernel->schedulingTest(argv[2]);
    }
    if (consoleTestFlag) {
      kernel->ConsoleTest();   // interactive test of the synchronized console
    }
    if (synchTestFlag){
      kernel->synchTest();
    }
    if (networkTestFlag) {
      kernel->NetworkTest();   // two-machine test of the network
    }
    if (shellFlag)
    {
    	kernel->Shell();
    }

#ifndef FILESYS_STUB
    if (createFileName != NULL) {
    	kernel->fileSystem->Create(createFileName, initialSize);
       }
    if (removeFileName != NULL) {
      kernel->fileSystem->Remove(removeFileName);
    }
    if (copyUnixFileName != NULL && copyNachosFileName != NULL) {
      kernel->fileSystem->CopyFileFromUnix(copyUnixFileName,copyNachosFileName);
    }
    if (copyNachosFile1 != NULL && copyNachosFile2 != NULL) {
    	kernel->fileSystem->NachosfileCopy(copyNachosFile1,copyNachosFile2);
    }

    if (nachosfileName1 != NULL && nachosfileName2 != NULL) {
    	kernel->fileSystem->ChangefileName(nachosfileName1,nachosfileName2);
    }

    if (appendUnixFileName != NULL && appendNachosFileName != NULL) {
      kernel->fileSystem->AppendFileFromUnix(appendUnixFileName,appendNachosFileName);
    }
    if (dumpFlag) {
      kernel->fileSystem->Print();
    }
    if (dirListFlag) {
      kernel->fileSystem->List();
    }
    if (printFileName != NULL) {
      kernel->fileSystem->PrintFile(printFileName);
    }
    if (lsDirName != NULL) {
      kernel->fileSystem->List(lsDirName);
    }
    if (mkDirName != NULL) {
      kernel->fileSystem->Mkdir(mkDirName);
    }
    if (rmDirName != NULL) {
        kernel->fileSystem->Rmdir(rmDirName);
    }
#endif // FILESYS_STUB

    // finally, run an initial user program if requested to do so
    if (userProgName != NULL) {
      DEBUG(dbgGxc,"step1---in main\n");
      AddrSpace *space = new AddrSpace(kernel->fileSystem->Open(userProgName));
      DEBUG(dbgGxc,"step3---back in main\n");
      ASSERT(space != (AddrSpace *)NULL);
   //   if (space->Load(userProgName)) {  // load the program into the space
	space->Execute();              // run the program
	ASSERTNOTREACHED();            // Execute never returns
   //   }
    }

    // If we don't run a user program, we may get here.
    // Calling "return" would terminate the program.
    // Instead, call Halt, which will first clean up, then
    //  terminate.
    kernel->interrupt->Halt();

    ASSERTNOTREACHED();
}

